package com.lp.security.interceptor;

import cn.hutool.json.JSONUtil;
import com.lp.biz.member.model.entity.Member;
import com.lp.biz.member.service.MemberService;
import com.lp.common.component.RedisService;
import com.lp.common.context.ContextHolder;
import com.lp.common.context.UserDetail;
import com.lp.common.exception.ErrorCodeEnum;
import com.lp.common.utils.CommonResult;
import com.lp.common.utils.RsaUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.lp.common.constant.CacheConstant.TOKEN_KEY;
import static com.lp.common.constant.RoleConstant.ADMIN;

/**
 * @program: backend
 * @description:
 * @author: Ke.Song
 * @since: 2024-03-11 21:11:52
 */
@Configuration
public class AdminInterceptor implements HandlerInterceptor {

    @Value("${token.tokenHeader}")
    private String tokenHeader;

    @Value("${token.tokenHead}")
    private String tokenHead;

    /**
     * rsa随机数
     */
    @Value("${rsa.randomNum}")
    private String randomNum;

    /**
     * Token有效期
     */
    @Value("${token.expiration}")
    private Integer expiration;

    @Autowired
    private RedisService redis;

    /**
     * rsa 私钥，解密前台公钥加密的数据
     */
    @Value("${rsa.privateKey}")
    private String privateKey;

    @Autowired
    private MemberService memberService;

    private static final Logger log = LogManager.getLogger(AdminInterceptor.class);

    private static final String PATTERN = "^([0-9]+):%s$";

    /**
     * 请求前拦截器。校验身份、权限
     */
    @Override
    public boolean preHandle(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) throws Exception {
        ErrorCodeEnum errorCodeEnum = ErrorCodeEnum.AUTHORIZED_FAILED;
        if (request != null) {
            if (authorizationFilter(request)) {
                if (permissionFilter(request)) {
                    return true;
                } else {
                    errorCodeEnum = ErrorCodeEnum.PERMISSION_ERROR;
                }
            }
        }
        if (response != null) {
            response(response, errorCodeEnum);
        }
        return false;
    }

    /**
     * 资格认证
     */
    private boolean authorizationFilter(HttpServletRequest request) throws Exception {
        String header = request.getHeader(tokenHeader);
        boolean result = false;
        if (StringUtils.isNotBlank(header)) {
            String token = header.replace(tokenHead + " ", "");
            String id;
            try {
                id = RsaUtils.decrypt(token, privateKey).replace(":" + randomNum, "");
            } catch (Exception e) {
                return false;
            }
            if (StringUtils.isNotBlank(id)) {
                String cacheToken = redis.get(TOKEN_KEY + id);
                if (cacheToken != null && cacheToken.equals(token)) {
                    Member member = memberService.getById(id);
                    if (member != null) {
                        ContextHolder.getContext().setMemberId(member.getId());
                        UserDetail userDetail = memberService.getUserDetail(null);
                        ContextHolder.getContext().setUserDetail(userDetail);
                        result = true;
                    }
                }
            }
        }
        return result;
    }

    private boolean permissionFilter(HttpServletRequest request) {
        boolean result = false;
        String path = request.getMethod() + ":" + request.getServletPath();
        UserDetail userDetail = ContextHolder.getContext().getUserDetail();
        if (userDetail.getRoles().contains(ADMIN)) {
            result = true;
        } else {
            log.info("filter: 开始校验URL");
            for (String endpoint : userDetail.getEndpoints()) {
                Matcher matcher = Pattern.compile("^" + endpoint + "$").matcher(path);
                if (matcher.find()) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    /**
     * 请求后拦截器。清除当前用户信息
     */
    @Override
    public void postHandle(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler, ModelAndView modelAndView) {
        ContextHolder.clearContext();
    }

    /**
     * 格式化响应
     *
     * @param response      响应体
     * @param errorCodeEnum 错误码
     * @throws IOException 异常
     */
    private void response(HttpServletResponse response, ErrorCodeEnum errorCodeEnum) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubdomains;preload");
        final CommonResult error = CommonResult.error(errorCodeEnum.getBizCode(), errorCodeEnum.getMsg());
        response.getWriter().println(JSONUtil.parse(error));
        response.getWriter().flush();
    }
}
